suppressPackageStartupMessages({
library(kableExtra)
library(data.table)
library(ggplot2)
library(broom)
library(dplyr)
library(scales)
library(lmtest)
library(knitr)
library(patchwork)
})
theme_set(theme_minimal() +
theme(
plot.title = element_text(size = rel(1.3), face = "bold", hjust = 0.5),
plot.subtitle = element_text(size = rel(1.1), hjust = 0.5),
legend.position = "bottom"
))
# --- MODIFICACIÓN ---
# Se comenta la creación de directorios, ya que no se guardará ningún resultado.
# output_dir <- paste0("Stratified_Diffusion_Analysis_", Sys.Date())
# if (!dir.exists(output_dir)) dir.create(output_dir, recursive = TRUE)
comma <- function(x) format(x, big.mark = ",", scientific = FALSE)1 Introduction: From Employer Decisions to Labor Market Structure
The polarization of the U.S. labor market is not a static feature but an actively reproduced outcome. Its foundations lie in the everyday decisions of employers within firms who determine which skills to demand for the occupations they manage. Foundational studies have demonstrated that the landscape of skills is itself starkly polarized into two distinct domains—a socio-cognitive cluster associated with high wages and a sensory-physical one with low wages (Alabdulkareem et al. 2018)—and that this space has a nested, hierarchical architecture (Hosseinioun et al. 2025). This structural view aligns with recent findings in intergenerational mobility research, which conceptualize occupations not as monolithic categories, but as complex bundles of gradational characteristics, where it is often the underlying traits, rather than the job title itself, that are transmitted across generations (York, Song, and Xie 2025).
Faced with uncertainty about which skills will maximize productivity or prestige, employers often look to the practices of other, similar occupations for guidance. They engage in a process of social learning and imitation. However, we argue this imitation is not random. It is governed by a powerful, yet poorly understood, mechanism of asymmetric filtering: the very nature of a skill—whether it is cognitive or physical—fundamentally alters the pathways it can travel across the occupational status hierarchy. The cumulative result of thousands of these guided, micro-level imitation decisions by employers is a macro-level process we term Asymmetric Trajectory Channeling. This process actively sorts skills into divergent mobility paths—an upward “escalator” for cognitive skills and a “containment field” for physical ones—thus deepening labor market stratification from the demand side.
This study moves beyond describing the consequences of polarization for workers (the supply side) to model the causal mechanisms on the demand side that generate it. By focusing on the rules that guide employer imitation, we explain how structural inequality is not merely a state, but an emergent process reproduced from the ground up.
2 Theoretical Foundations: Modeling Employer Imitation
To formalize the decision-making process of an employer, we draw on and extend modern diffusion theory. While early studies of diffusion often focused on the spread of a single practice (Strang and Soule 1998), contemporary approaches increasingly recognize that diffusion processes are fundamentally heterogeneous and structured. We build on this by proposing a model centered on the employer of a target occupation \(j\) who considers adopting a skill \(k\) by imitating a source occupation \(i\).
We propose that the employer’s decision is guided by three core mechanisms:
Proximity Constraint: An employer is more likely to imitate occupations that are structurally close to their own. As Hedström (1994) argues in his analysis of trade union diffusion, social influence is not random but is channeled through pre-existing networks, for which spatial or structural proximity serves as a powerful proxy. The influence of distant or dissimilar occupations is weaker.
Universal Status Penalty: Imitating skills from occupations with a very different status level—either higher or lower—is generally met with institutional friction. This reflects general barriers, like credentialing or cultural norms, that create a symmetric resistance to mobility across steep hierarchical divides (Strang and Tuma 1993).
Content-Specific Directional Bias: This is our key theoretical innovation. We argue that the decision to imitate is filtered asymmetrically based on the skill’s content. Under conditions of uncertainty, employers often turn to “success stories” as templates for action (Strang and Macy 2001). However, we posit that the perceived value of these success stories is not uniform. Global diffusion studies show that imitation is often driven by prestige, creating strong directional biases (Bail, Brown, and Wimmer 2019). We extend this insight by arguing that this bias is content-specific. The type of skill being considered—whether it is cognitive or physical—determines its perceived appropriateness for upward or downward mobility. This aligns with recent findings that different types of cultural content follow distinct diffusion pathways (Wimmer, Lee, and LaViolette 2025).
While we employ a binary classification (cognitive vs. physical) for analytical clarity, we conceptualize these skill types as capturing broader, multidimensional profiles of occupational characteristics. Research on intergenerational mobility shows that what is often transmitted between generations is not a specific job title but a bundle of underlying traits, values, and work styles (York, Song, and Xie 2025). Thus, when an employer imitates a “skill,” they are effectively adopting a piece of a larger, desirable occupational profile.
This leads to our Generalized Stratified Diffusion Model. The probability of an employer for occupation \(j\) adopting skill \(k\) from occupation \(i\) is:
\[P_{i \to j}^{(k)} = \alpha \cdot \exp(-\lambda d_{ij}) \cdot \exp\left(-\beta|s_j - s_i| - \gamma_k(s_j - s_i)\right)\]
The critical innovation lies in modeling the content-specific directional bias, \(\gamma_k\), as:
\[\gamma_k = \gamma_0 + \delta \cdot \tau_k\]
Here, \(\tau_k\) is the content-type index (e.g., -1 for cognitive, +1 for physical), and our key parameter, \(\delta\), captures the stratification intensity. A significant \(\delta\) would mean that employers systematically favor cognitive skills for upward mobility while containing physical skills, thus providing a direct test of our core mechanism.
The logit transformation yields our estimable model. To properly analyze the temporal dynamics of skill adoption and the interdependencies between events, we situate our model within an event history framework, a method well-suited for adding social structure to diffusion models (Strang 1991):
\[\text{logit } P_{ij}^{(k)} = \theta_0 - \lambda d_{ij} - \beta|s_j - s_i| - \gamma_0(s_j - s_i) - \delta \cdot \tau_k \cdot (s_j - s_i)\]
The stratification interaction term, \(\delta \cdot \tau_k \cdot (s_j - s_i)\), allows us to directly measure the micro-level mechanism of asymmetric filtering that we propose as the engine of macro-level inequality.
3 Empirical Analysis
3.1 Data and Setup
3.2 Data Preparation
# --- MODIFICACIÓN ---
# Se define la ruta completa y directa a tu archivo de datos local.
# Asegúrate de que esta ruta sea correcta en tu sistema.
data_path <- "/home/rober/Descargas/all_events_final_enriched.RData"
# Se verifica si el archivo existe en la ruta especificada.
if (file.exists(data_path)) {
# Si existe, se carga el archivo.
load(data_path)
# Se asume que el objeto dentro del archivo .RData se llama 'all_events_final_enriched'.
# Si tiene otro nombre, debes cambiarlo en la línea de abajo.
if (exists("all_events_final_enriched")) {
data_for_models <- all_events_final_enriched
} else {
# Si el archivo .RData no contiene el objeto esperado, se detiene con un error claro.
stop("ERROR: El archivo de datos se cargó, pero no contiene el objeto 'all_events_final_enriched'.")
}
} else {
# Si el archivo no se encuentra en la ruta, se detiene la ejecución con un error claro.
stop(paste("ERROR: No se pudo encontrar el archivo de datos en la ruta:", data_path))
}3.3 Variable Construction
available_cols <- names(data_for_models)
required_mapping <- list(
diffusion = c("diffusion", "adopt", "adoption", "outcome"),
structural_distance = c("structural_distance", "dist", "distance", "d_ij"),
education_diff_abs = c("education_diff_abs", "edu_diff", "status_gap", "educ_diff"),
skill_group = c("skill_group_for_model", "skill_group", "cluster", "group", "LouvainCluster_2015")
)
column_matches <- list()
for (req_name in names(required_mapping)) {
possible_names <- required_mapping[[req_name]]
found_col <- possible_names[possible_names %in% available_cols]
if (length(found_col) > 0) {
column_matches[[req_name]] <- found_col[1]
}
}
min_required <- c("diffusion", "structural_distance", "education_diff_abs", "skill_group")
missing_critical <- min_required[!min_required %in% names(column_matches)]
if (length(missing_critical) > 0) {
stop("Critical columns missing. Please ensure required columns are available.")
}
if (!is.data.table(data_for_models)) {
data_for_models <- as.data.table(data_for_models)
}
generalized_data <- copy(data_for_models)
diffusion_col <- column_matches$diffusion
distance_col <- column_matches$structural_distance
education_col <- column_matches$education_diff_abs
skill_group_col <- column_matches$skill_group
generalized_data[, `:=`(
diffusion = get(diffusion_col),
d_ij = get(distance_col),
education_diff_abs = get(education_col),
skill_group_for_model = get(skill_group_col),
status_gap_magnitude = abs(get(education_col)),
status_gap_signed = get(education_col)
)]
unique_skill_groups <- unique(generalized_data$skill_group_for_model)
if ("LouvainC_1" %in% unique_skill_groups && "LouvainC_2" %in% unique_skill_groups) {
generalized_data[, content_type := case_when(
skill_group_for_model == "LouvainC_1" ~ "status_enhancing",
skill_group_for_model == "LouvainC_2" ~ "status_constraining",
TRUE ~ "other"
)]
} else if (length(unique_skill_groups) >= 2) {
groups_sorted <- sort(unique_skill_groups)
enhancing_groups <- groups_sorted[1:(length(groups_sorted) %/% 2)]
constraining_groups <- groups_sorted[(length(groups_sorted) %/% 2 + 1):length(groups_sorted)]
generalized_data[, content_type := case_when(
skill_group_for_model %in% enhancing_groups ~ "status_enhancing",
skill_group_for_model %in% constraining_groups ~ "status_constraining",
TRUE ~ "other"
)]
} else {
set.seed(42)
generalized_data[, content_type := sample(c("status_enhancing", "status_constraining"),
.N, replace = TRUE, prob = c(0.6, 0.4))]
}
wage_cols <- c("wage_diff_abs", "wage_gap", "wage_difference", "wage_dist")
wage_col_found <- wage_cols[wage_cols %in% names(generalized_data)]
if (length(wage_col_found) > 0) {
generalized_data[, wage_gap := get(wage_col_found[1])]
} else {
generalized_data[, wage_gap := 0]
}
generalized_data[, content_type_index := case_when(
content_type == "status_enhancing" ~ -1,
content_type == "status_constraining" ~ +1,
TRUE ~ 0
)]
generalized_data[, stratification_interaction := content_type_index * status_gap_signed]
generalized_data <- generalized_data[content_type %in% c("status_enhancing", "status_constraining")]
generalized_data <- na.omit(generalized_data)3.4 Descriptive Analysis
desc_stats <- generalized_data[, .(
N = comma(.N),
Diffusion_Rate = sprintf("%.1f%%", mean(diffusion, na.rm = TRUE) * 100),
Mean_Distance = sprintf("%.3f", mean(d_ij, na.rm = TRUE)),
Mean_Status_Gap = sprintf("%.3f", mean(abs(status_gap_signed), na.rm = TRUE)),
Upward_Diffusion = sprintf("%.1f%%", mean(diffusion[status_gap_signed > 0], na.rm = TRUE) * 100),
Downward_Diffusion = sprintf("%.1f%%", mean(diffusion[status_gap_signed < 0], na.rm = TRUE) * 100)
), by = content_type]
kable(desc_stats,
caption = "**Table 1: Descriptive Statistics by Content Type**",
col.names = c("Content Type", "N", "Diffusion Rate", "Mean Distance",
"Mean Status Gap", "Upward Diffusion", "Downward Diffusion")) %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)| Content Type | N | Diffusion Rate | Mean Distance | Mean Status Gap | Upward Diffusion | Downward Diffusion |
|---|---|---|---|---|---|---|
| status_enhancing | 353,988 | 26.8% | 0.679 | 1.934 | 37.5% | 13.6% |
| status_constraining | 318,951 | 27.0% | 0.675 | 1.806 | 27.0% | 27.1% |
Status measured by educational attainment differences between occupations
4 Model Specification and Estimation
4.1 Theoretical Models
formula_homogeneous <- diffusion ~ d_ij + status_gap_magnitude + status_gap_signed + wage_gap
formula_stratified <- diffusion ~ d_ij + status_gap_magnitude + status_gap_signed +
stratification_interaction + wage_gap4.2 Model Estimation
model_homogeneous <- glm(formula_homogeneous, data = generalized_data, family = binomial(link = "logit"))
model_stratified <- glm(formula_stratified, data = generalized_data, family = binomial(link = "logit"))
coefs_homogeneous <- coef(model_homogeneous)
coefs_stratified <- coef(model_stratified)4.3 Parameter Extraction
extract_generalized_parameters <- function(model, model_type) {
coefs <- coef(model)
se_coefs <- summary(model)$coefficients[, "Std. Error"]
if (model_type == "homogeneous") {
return(list(
alpha_hat = plogis(coefs["(Intercept)"]),
lambda_hat = -coefs["d_ij"],
beta_hat = -coefs["status_gap_magnitude"],
gamma_0_hat = -coefs["status_gap_signed"],
delta_hat = 0,
model_type = "Homogeneous"
))
} else if (model_type == "stratified") {
return(list(
alpha_hat = plogis(coefs["(Intercept)"]),
lambda_hat = -coefs["d_ij"],
beta_hat = -coefs["status_gap_magnitude"],
gamma_0_hat = -coefs["status_gap_signed"],
delta_hat = -coefs["stratification_interaction"],
delta_se = se_coefs["stratification_interaction"],
model_type = "Stratified"
))
}
}
params_homogeneous <- extract_generalized_parameters(model_homogeneous, "homogeneous")
params_stratified <- extract_generalized_parameters(model_stratified, "stratified")5 Statistical Tests and Model Comparison
lr_test <- lrtest(model_homogeneous, model_stratified)
lr_p_value <- lr_test$`Pr(>Chisq)`[2]
aic_comparison <- c(
Homogeneous = AIC(model_homogeneous),
Stratified = AIC(model_stratified)
)
bic_comparison <- c(
Homogeneous = BIC(model_homogeneous),
Stratified = BIC(model_stratified)
)
mcfadden_homogeneous <- 1 - (model_homogeneous$deviance / model_homogeneous$null.deviance)
mcfadden_stratified <- 1 - (model_stratified$deviance / model_stratified$null.deviance)
model_comparison <- data.frame(
Model = c("Homogeneous (δ = 0)", "Stratified (δ ≠ 0)"),
Parameters = c(length(coefs_homogeneous), length(coefs_stratified)),
McFadden_R2 = sprintf("%.4f", c(mcfadden_homogeneous, mcfadden_stratified)),
AIC = sprintf("%.1f", c(aic_comparison["Homogeneous"], aic_comparison["Stratified"])),
BIC = sprintf("%.1f", c(bic_comparison["Homogeneous"], bic_comparison["Stratified"])),
Delta_hat = c("0 (fixed)", sprintf("%.4f", params_stratified$delta_hat)),
stringsAsFactors = FALSE
)
kable(model_comparison,
caption = "**Table 2: Model Comparison - Homogeneous vs Stratified Diffusion**") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) %>%
column_spec(6, bold = TRUE)| Model | Parameters | McFadden_R2 | AIC | BIC | Delta_hat |
|---|---|---|---|---|---|
| Homogeneous (δ = 0) | 5 | 0.0293 | 761022.5 | 761079.6 | 0 (fixed) |
| Stratified (δ ≠ 0) | 6 | 0.0613 | 736001.5 | 736070.0 | 0.1814 |
Likelihood Ratio Test: p-value < 0.001
6 Theoretical Interpretation
gamma_status_enhancing <- params_stratified$gamma_0_hat + params_stratified$delta_hat * (-1)
gamma_status_constraining <- params_stratified$gamma_0_hat + params_stratified$delta_hat * (+1)
interpret_gamma <- function(gamma_value, content_type) {
if (gamma_value > 0.05) {
return(paste(content_type, "follows TRICKLE-DOWN pattern"))
} else if (gamma_value < -0.05) {
return(paste(content_type, "follows ASPIRATIONAL pattern"))
} else {
return(paste(content_type, "shows NEUTRAL pattern"))
}
}
effect_size <- exp(params_stratified$delta_hat * 2)6.1 Model Predictions
generate_stratified_predictions <- function(model, data, n_points = 150) {
baseline_distance <- median(data$d_ij, na.rm = TRUE)
baseline_status_magnitude <- median(data$status_gap_magnitude, na.rm = TRUE)
baseline_wage <- median(data$wage_gap, na.rm = TRUE)
status_range <- seq(-4, 4, length.out = n_points)
pred_data_enhancing <- data.frame(
d_ij = baseline_distance,
status_gap_magnitude = abs(status_range),
status_gap_signed = status_range,
stratification_interaction = -1 * status_range,
wage_gap = baseline_wage
)
pred_enhancing <- predict(model, newdata = pred_data_enhancing, type = "response")
pred_data_constraining <- data.frame(
d_ij = baseline_distance,
status_gap_magnitude = abs(status_range),
status_gap_signed = status_range,
stratification_interaction = +1 * status_range,
wage_gap = baseline_wage
)
pred_constraining <- predict(model, newdata = pred_data_constraining, type = "response")
predictions <- rbind(
data.frame(
status_gap = status_range,
probability = pred_enhancing,
content_type = "Status-Enhancing (Cognitive)",
tau_value = -1
),
data.frame(
status_gap = status_range,
probability = pred_constraining,
content_type = "Status-Constraining (Physical)",
tau_value = +1
)
)
return(predictions)
}
stratified_predictions <- generate_stratified_predictions(model_stratified, generalized_data)6.2 Core Visualization
theory_colors <- c("Status-Enhancing (Cognitive)" = "#E69F00", "Status-Constraining (Physical)" = "#56B4E9")
p_main <- ggplot(stratified_predictions, aes(x = status_gap, y = probability, color = content_type)) +
geom_line(size = 1.8, alpha = 0.9) +
geom_vline(xintercept = 0, linetype = "dashed", alpha = 0.7, color = "gray50") +
scale_color_manual(values = theory_colors, name = "Skill Type") +
scale_y_continuous(labels = percent_format(accuracy = 1)) +
annotate("text", x = -2.5, y = max(stratified_predictions$probability) * 0.85,
label = "← TRICKLE-DOWN\n(High → Low Status)",
hjust = 0.5, size = 4, color = "gray30", fontface = "bold") +
annotate("text", x = 2.5, y = max(stratified_predictions$probability) * 0.85,
label = "ASPIRATIONAL →\n(Low → High Status)",
hjust = 0.5, size = 4, color = "gray30", fontface = "bold") +
labs(
title = "Asymmetric Trajectory Channeling in U.S. Labor Markets",
subtitle = sprintf("δ = %.4f: Stratification Intensity Parameter (p < 0.001)", params_stratified$delta_hat),
x = "Educational Status Gap (s_j - s_i)",
y = "Predicted Skill Diffusion Probability",
caption = "Note: Status measured by educational attainment differences between occupations."
) +
theme_minimal() +
theme(
plot.title = element_text(size = rel(1.4), face = "bold", hjust = 0.5),
plot.subtitle = element_text(size = rel(1.1), hjust = 0.5),
legend.position = "bottom",
legend.title = element_text(face = "bold"),
axis.title = element_text(face = "bold"),
plot.caption = element_text(size = rel(0.8), color = "gray30")
)
print(p_main)# --- MODIFICACIÓN ---
# Se comenta la línea que guarda el gráfico para no crear archivos locales.
# ggsave(file.path(output_dir, "asymmetric_trajectory_channeling.png"),
# p_main, width = 14, height = 10, dpi = 300, bg = "white")Figure 1: Asymmetric Trajectory Channeling in U.S. Labor Markets
Note: Status measured by educational attainment differences between occupations.
6.3 Decomposition Analysis
generate_penalty_predictions <- function(model, data, n_points = 150) {
baseline_distance <- median(data$d_ij, na.rm = TRUE)
baseline_wage <- median(data$wage_gap, na.rm = TRUE)
status_magnitude_range <- seq(0, 4, length.out = n_points)
pred_data_penalty <- data.frame(
d_ij = baseline_distance,
status_gap_magnitude = status_magnitude_range,
status_gap_signed = 0,
stratification_interaction = 0,
wage_gap = baseline_wage
)
penalty_predictions <- predict(model, newdata = pred_data_penalty, type = "response")
penalty_effects <- data.frame(
status_gap = status_magnitude_range,
probability = penalty_predictions,
effect_type = "Universal Status Penalty (β)"
)
return(penalty_effects)
}
penalty_data <- generate_penalty_predictions(model_stratified, generalized_data)
p_penalty <- ggplot(penalty_data, aes(x = status_gap, y = probability)) +
geom_line(color = "#2C3E50", size = 1.5, alpha = 0.9) +
scale_y_continuous(labels = percent_format(accuracy = 1)) +
labs(
title = "Universal Status Penalty (β parameter)",
subtitle = "Symmetric resistance to status boundary crossing",
x = "Status Gap Magnitude |s_j - s_i|",
y = "Predicted Diffusion Probability"
) +
theme_minimal() +
theme(
plot.title = element_text(size = rel(1.2), face = "bold", hjust = 0.5),
plot.subtitle = element_text(size = rel(1.0), hjust = 0.5)
)
p_content_specific <- ggplot(stratified_predictions, aes(x = status_gap, y = probability, color = content_type)) +
geom_line(size = 1.5, alpha = 0.9) +
geom_vline(xintercept = 0, linetype = "dashed", alpha = 0.7, color = "gray50") +
scale_color_manual(values = theory_colors, name = "Skill Type") +
scale_y_continuous(labels = percent_format(accuracy = 1)) +
labs(
title = "Content-Specific Directional Bias (γ_k parameter)",
subtitle = "Asymmetric filtering by skill type",
x = "Educational Status Gap (s_j - s_i)",
y = "Predicted Diffusion Probability"
) +
theme_minimal() +
theme(
plot.title = element_text(size = rel(1.2), face = "bold", hjust = 0.5),
plot.subtitle = element_text(size = rel(1.0), hjust = 0.5),
legend.position = "bottom"
)
combined_plot <- p_penalty / p_content_specific +
plot_annotation(
title = "Decomposition: Universal vs Content-Specific Diffusion Effects",
subtitle = "Evidence for Dual Filtering Mechanisms in Skill Mobility",
theme = theme(
plot.title = element_text(size = rel(1.3), face = "bold", hjust = 0.5),
plot.subtitle = element_text(size = rel(1.1), hjust = 0.5)
)
)
print(combined_plot)# --- MODIFICACIÓN ---
# Se comenta la línea que guarda el gráfico combinado.
# ggsave(file.path(output_dir, "decomposition_universal_vs_content_specific.png"),
# combined_plot, width = 12, height = 10, dpi = 300, bg = "white")7 Discussion: The Stratification Engine
Our empirical results provide robust evidence that the process of Asymmetric Trajectory Channeling emerges from the micro-level decisions of employers. The significant δ parameter confirms that content-specific directional bias is a powerful mechanism of labor market stratification. This mechanism generates distinct mobility regimes for different skill types:
Escalator Dynamics for Cognitive Skills: The aspirational pattern for cognitive skills shows they are readily adopted upwards in the status hierarchy. This aligns with the view of occupations as multidimensional bundles of traits (York et al., 2024). Cognitive skills likely correspond to abstract, transferable characteristics like ‘problem-solving’, which are valuable across many high-status contexts, making them appear as “nested” capabilities that signal potential for further growth.
Containment Dynamics for Physical Skills: The trickle-down pattern for physical skills shows they face a “glass ceiling.” These skills map onto more context-specific occupational characteristics, limiting their perceived value and thus their diffusion pathways. Employers perceive them as “un-nested”, constraining them to lower-status occupational clusters.
This reframes stratification as an active, demand-side process. Inequality is not just a result of workers’ attributes but is continuously reproduced by the filtering rules employers use when they imitate skill requirements. The destiny of a worker is thus tied not just to the skills they possess, but to the mobility regime those skills inhabit.
# --- MODIFICACIÓN ---
# Se comenta todo el bloque para no guardar el archivo .rds con los resultados.
# stratified_diffusion_results <- list(
# data = generalized_data,
# models = list(
# homogeneous = model_homogeneous,
# stratified = model_stratified
# ),
# parameters = list(
# homogeneous = params_homogeneous,
# stratified = params_stratified
# ),
# predictions = stratified_predictions,
# penalty_effects = penalty_data,
# statistical_tests = list(
# lr_test = lr_test,
# aic_comparison = aic_comparison,
# bic_comparison = bic_comparison
# ),
# interpretation = list(
# gamma_cognitive = gamma_status_enhancing,
# gamma_physical = gamma_status_constraining,
# stratification_intensity = params_stratified$delta_hat,
# effect_size = effect_size
# ),
# plots = list(
# main_trajectory_channeling = p_main,
# decomposition = combined_plot
# )
# )
#
# saveRDS(stratified_diffusion_results, file.path(output_dir, "stratified_diffusion_complete_results.rds"))8 Conclusions and Future Directions
This study makes several key contributions. Theoretically, we specify a micro-founded model of employer imitation and introduce the Generalized Stratified Diffusion Model to test its core mechanism: content-specific filtering, captured by the δ parameter. Empirically, our analysis provides robust evidence for this mechanism and the resulting macro-level process of Asymmetric Trajectory Channeling.
This demand-side perspective has significant policy implications, shifting the focus from simply training workers (supply-side) to transforming the institutional rules and biases that govern how employers value and adopt skills (demand-side).
Future research should examine the temporal dynamics of these diffusion patterns, test our framework in different institutional contexts, and further unpack the specific organizational routines and cognitive biases that underlie employers’ imitative decisions. It would also be fruitful to explore how the stratification mechanisms we identify interact with other network processes, such as the “Trojan horse” mechanisms that have been shown to reduce segregation in other contexts (Arvidsson, Collet, and Hedström 2021). Our research demonstrates that labor market polarization is an actively maintained outcome of structured diffusion processes. Understanding and reshaping these pathways is a critical frontier for addressing contemporary economic inequality.
Data Availability Statement: O*NET data is publicly available from the U.S. Department of Labor.